#ifndef __CROSSPLATFORM_H__
#define __CROSSPLATFORM_H__

#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#include <Ws2tcpip.h>
typedef HANDLE pthread_t;
#else
#include <pthread.h>
#endif

bool SpawnThread(void* (*fn)(void*), void *arg, pthread_t *thread);
void* JoinThread(pthread_t h);


#ifdef WIN32
class NotificationEvent
{
public:
	NotificationEvent()
	{
		Event = CreateEvent(NULL, TRUE, FALSE, NULL);
	}
	~NotificationEvent()
	{
		CloseHandle(Event);
	}

	void Wait(unsigned int delay = INFINITE)
	{
		WaitForSingleObject(Event, delay);
	}
	void Signal()
	{
		SetEvent(Event);
	}
	void Clear()
	{
		ResetEvent(Event);
	}

private:
	HANDLE Event;
};

class CriticalSection
{
public:
	CriticalSection()
	{
		InitializeCriticalSection(&Lock);
	}
	~CriticalSection()
	{
		DeleteCriticalSection(&Lock);
	}

	void Acquire()
	{
		EnterCriticalSection(&Lock);
	}

	void Release()
	{
		LeaveCriticalSection(&Lock);
	}

private:
	CRITICAL_SECTION Lock;
};
#endif

class AutoCriticalSection
{
public:
	AutoCriticalSection(CriticalSection& cs) : Lock(cs) { cs.Acquire(); }
	~AutoCriticalSection() { Lock.Release(); }

private:
	CriticalSection& Lock;
};

template<typename T>
class ProducerConsumerFifo
{
public:
	ProducerConsumerFifo() : Head(0), Tail(0), IsDead(false) {}
	~ProducerConsumerFifo() {}

	bool Queue(T *e)
	{
		e->Next = 0;
		Lock.Acquire();
		if(IsDead)
			return false;
		if(Head) {
			Tail->Next = e;
		} else {
			Head = e;
			NotEmpty.Signal();
		}
		Tail = e;
		Lock.Release();
		return true;
	}
	T* DequeueWait()
	{
		for(;;) {
			if(Head) { //yeah, double-checked locking, but it's volatile so it should be all good
				AutoCriticalSection cs(Lock);
				T *e = Head;
				if(e) {
					Head = e->Next;
					if(!e->Next)
						NotEmpty.Clear();
					return e;
				}
			}
			if(IsDead)
				return NULL;
			NotEmpty.Wait();
		}
	}

	void Dead()
	{
		AutoCriticalSection cs(Lock);
		IsDead = true;
		NotEmpty.Signal();
	}

private:
	CriticalSection Lock;
	NotificationEvent NotEmpty;
	T * volatile Head;
	T *Tail;
	volatile bool IsDead;
};



#endif
